/*
 * Project: Sudoku Explainer
 * Copyright (C) 2006-2007 Nicolas Juillerat
 * 
 * Project: Sudoku Hint Engine for Android
 * Copyright (C) 2010-2013 Emanuel Moecklin
 * 
 * Available under the terms of the Lesser General Public License (LGPL)
 */
package com.onegravity.sudoku.solver.rules.uniqueness;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.net.Uri;

import com.onegravity.sudoku.solver.HintResource;
import com.onegravity.sudoku.solver.HintType;
import com.onegravity.sudoku.solver.R;
import com.onegravity.sudoku.solver.core.IndirectHint;
import com.onegravity.sudoku.solver.core.Rule;
import com.onegravity.sudoku.solver.data.Cell;
import com.onegravity.sudoku.solver.util.Link;

public abstract class UniquenessTestHint extends IndirectHint implements Rule {

	private static final long serialVersionUID = -951105340183306464L;
	
	protected final List<Cell> loop;
    protected final int v1;
    protected final int v2;

    protected static final int[] setNamesRes = {R.string.hint_pair, R.string.hint_triple, R.string.hint_quad, R.string.hint_set5, R.string.hint_set6, R.string.hint_set7};

    public UniquenessTestHint(List<Cell> loop, int v1, int v2, Map<Cell, BitSet> removablePotentials) {
        super(removablePotentials);
        this.loop = loop;
        this.v1 = v1;
        this.v2 = v2;
    }

    @Override
    protected Cell[] getSelectedCells() {
        Cell[] cells = new Cell[loop.size()];
        loop.toArray(cells);
        return cells;
    }

    @Override
    public Map<Cell, BitSet> getSelectedPotentials(HintType hintType, int viewNum) {
        Map<Cell, BitSet> result = new HashMap<Cell, BitSet>();
        for (Cell cell : loop) {
            BitSet commonValues = new BitSet(10);
            commonValues.set(v1);
            commonValues.set(v2);
            result.put(cell, commonValues);
        }
        return result;
    }

    @Override
    public Collection<Link> getLinks(HintType type, int viewNum) {
        Collection<Link> result = new ArrayList<Link>();
        for (int i = 0; i < loop.size(); i++) {
            Cell cell = loop.get(i);
            Cell next = loop.get((i + 1) % loop.size());
            result.add(new Link(cell, 0, next, 0));
        }
        return (type==HintType.HINT) ? result : null;
    }

    @Override
    public int getNrOfSteps() {
        return 3;
    }

    @Override
    public String getName() {
    	return getResource().getName(getType()) + getAppendix();
    }

    @Override
    public Uri getLink() {
    	return getResource().getUrl();
	}

    protected final String getTypeName() {
        return HintResource.getString((loop.size() == 4) ? R.string.hint_rectangle : R.string.hint_loop);
    }

    protected abstract int getType();

    protected String getAppendix() {
    	return ""; //$NON-NLS-1$
    }

    protected abstract HintResource getResource();

    @Override
    public double getDifficulty() {
        double result = 4.5;
        if (loop.size()>=10) result += 0.3;
        if (loop.size()>=8) result += 0.2;
        else if (loop.size()>=6) result += 0.1;
        return result; // 4.5 - 5.0
    }

    @Override
    public String toString() {
        Cell[] cells = new Cell[loop.size()];
        loop.toArray(cells);
        return getName() + ": " + Cell.toFullString(cells) + " " + v1 + ", " + v2; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) return false;
        if (!o.getClass().equals(this.getClass())) return false;
        UniquenessTestHint other = (UniquenessTestHint)o;
        if (this.loop.size() != other.loop.size()) return false;
        return this.loop.containsAll(other.loop);
    }

    @Override
    public int hashCode() {
        int result = 0;
        for (Cell cell : loop)
            result ^= cell.hashCode();
        return result;
    }

}